﻿namespace _002_Open_ClosedPrinciple
{
    internal class Program
    {

        /*
         *  开闭原则的核心思想是：

                对扩展开放（Open for extension）： 软件实体（类、模块、函数等）应该可以通过增加新的功能来进行扩展，而无需修改现有的源代码。
                对修改关闭（Closed for modification）： 一旦软件实体完成实现并投入使用，其源代码不应再被修改。
         *
         *
         */

        static void Main(string[] args)
        {
            var apple = new Product("Apple", Color.Blue, Size.Large);
            var orange = new Product("Orange", Color.Red, Size.Small);
            var iceCream = new Product("IceCream", Color.Green, Size.Medium);
            var tree = new Product("Tree", Color.Blue, Size.Large);


            Product[] products = { apple, orange, iceCream, tree };


            //var pf = new ProductFilter();
            //foreach (var product in pf.FilterByColor(products,Color.Blue))
            //{

            //    Console.WriteLine(product);
            //}


            var gf = new GoodFilter();
            var enumerable = gf.Filter(products, new AndSpecification<Product>(new ColorSpecification(Color.Blue),
                new SizeSpecification(Size.Large)));

            foreach (var product in enumerable)
            {
                Console.WriteLine(product);
            }



        }
    }



    #region MyRegion


    public class SizeSpecification : ISpecification<Product>
    {
        private readonly Size _size;

        public SizeSpecification(Size size)
        {
            _size = size;
        }

        public bool IsSatisfied(Product t)
        {
            return t.Size == _size;
        }
    }


    public class ColorSpecification : ISpecification<Product>
    {
        private readonly Color _color;

        public ColorSpecification(Color color)
        {
            _color = color;
        }

        public bool IsSatisfied(Product t)
        {
            return t.Color == _color;
        }
    }

    public class GoodFilter : IFilter<Product>
    {
        public IEnumerable<Product> Filter(IEnumerable<Product> t, ISpecification<Product> specification)
        {
            foreach (var product in t)
            {
                if (specification.IsSatisfied(product))
                {
                    yield return product;
                }
            }
        }
    }



    public interface ISpecification<in T>
    {
        bool IsSatisfied(T t);


    }


    public class AndSpecification<T> : ISpecification<T>
    {
        private ISpecification<T> first, second;


        public AndSpecification(ISpecification<T> first, ISpecification<T> second)
        {
            this.first = first;
            this.second = second;
        }

        public bool IsSatisfied(T t)
        {
            return first.IsSatisfied(t) && second.IsSatisfied(t);

        }
    }


    public interface IFilter<T>
    {
        IEnumerable<T> Filter(IEnumerable<T> t, ISpecification<T> specification);
    }


    #endregion
    public enum Color
    {
        Red,
        Green,
        Blue,
    }


    public enum Size
    {
        Small,
        Medium,
        Large
    }

    public class Product
    {
        public string Name { get; set; }

        public Color Color { get; set; }

        public Size Size { get; set; }

        public Product(string name, Color color, Size size)
        {
            Name = name;
            Color = color;
            Size = size;
        }


        public override string ToString()
        {
            return $"{Name}-{Color}-{Size}";
        }
    }

    public class ProductFilter
    {
        public IEnumerable<Product> FilterBySize(IEnumerable<Product> products, Size size)
        {
            return products.Where(x => x.Size == size);
        }


        public IEnumerable<Product> FilterByColor(IEnumerable<Product> products, Color color)
        {
            return products.Where(x => x.Color == color);
        }


    }
}
